package edu.osu.intelligentagents.assignment2;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Vector;

import edu.osu.intelligentagents.common.Action;
import edu.osu.intelligentagents.common.MDP;
import edu.osu.intelligentagents.common.State;
import edu.osu.intelligentagents.common.StationaryPolicy;

public class Assignment2_Parkinglot {
	private static boolean debug = false;

	public static MDP generateParkingScenario(String fileName) {
		try {
			BufferedReader bufferedReader = new BufferedReader(new FileReader(
					new File(fileName)));
			String line = bufferedReader.readLine();
			int n = Integer.valueOf(line);
			Vector<ParkingSpot> parkingSpots = new Vector<>();
			HashMap<String, ParkingSpot> parkingSpotsHashMap = new HashMap<>();
			line = bufferedReader.readLine();
			int index = 1;
			while (line != null) {
				ParkingSpot parkingSpot;
				if (index <= n / 2) {
					parkingSpot = new ParkingSpot("A", index,
							Float.valueOf(line));
				} else {
					parkingSpot = new ParkingSpot("B", index - (n / 2),
							Float.valueOf(line));
				}
				parkingSpots.add(parkingSpot);
				parkingSpotsHashMap.put(parkingSpot.getName(), parkingSpot);
				line = bufferedReader.readLine();
				index++;
			}
			bufferedReader.close();
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(
					new File("parking_mdp.txt")));
			MDP mdp = new MDP();
			index = 0;
			for (ParkingSpot parkingSpot : parkingSpots) {
				for (int i = 0; i < 4; i++) {
					State state = null;
					switch (i) {
					case 0:
						state = new State(index, "State:"
								+ parkingSpot.getName());
						state.occupied = false;
						state.parked = false;
						break;
					case 1:
						state = new State(index, "State:"
								+ parkingSpot.getName());
						state.occupied = false;
						state.parked = true;
						break;
					case 2:
						state = new State(index, "State:"
								+ parkingSpot.getName());
						state.occupied = true;
						state.parked = false;
						break;
					case 3:
						state = new State(index, "State:"
								+ parkingSpot.getName());
						state.occupied = true;
						state.parked = true;
						break;
					}
					state.setReward(-0.25f);
					if (parkingSpot.getIndex() == 1) {
						if (i == 1) {
							state.setReward(-1f);
						} else if (i == 3) {
							state.setReward(-2f);
						}
					} else {
						if (i == 1) {
							if (index < 2 * n) {
								// //// linear reward :
								// state.setReward(Math.abs(n / 2 - index / 4));
								// /// quadratic reward
								state.setReward(Math.abs(n / 2 - index / 4)
										* (Math.abs(n / 2 - index / 4)));
							} else {
								// //// linear reward :
								// state.setReward(Math.abs(n / 2
								// - (index - 2 * n) / 4));
								// /// quadratic reward
								state.setReward(Math.abs(n / 2
										- (index - 2 * n) / 4)
										* Math.abs(n / 2 - (index - 2 * n) / 4));
							}
						} else if (i == 3) {
							state.setReward(-2f);
						}
					}
					state.object = parkingSpot;
					index++;
					mdp.states.add(state);
				}
			}
			mdp.states.add(new State(index, "Terminate", 0));

			float[][] transition;

			// /////////////////////////////////////////////////////////////////////////////////////////////////////
			// Defining the transition function for action drive
			// ////////////////////////////////////////////////////////////////////////////////////////////////////

			transition = new float[mdp.states.size()][mdp.states.size()];
			for (int i = 0; i < mdp.states.size(); i++) {
				for (int j = 0; j < mdp.states.size(); j++) {
					transition[i][j] = 0;
					if (i == mdp.states.size() - 1
							&& j == mdp.states.size() - 1)
						transition[i][j] = 1;
				}
			}
			DecimalFormat decimalFormat = new DecimalFormat();
			decimalFormat.applyPattern(".#");

			int nState = mdp.states.size() - 1;
			for (int i = 0; i < mdp.states.size(); i++) {
				State state1 = (State) mdp.states.get(i);
				if (state1.getId() != 4 * n && state1.parked == false) {
					ParkingSpot parkingSpot1 = (ParkingSpot) state1.object;
					ParkingSpot nextParkingSpot;
					if (parkingSpot1.name.equalsIgnoreCase("A")) {
						if (parkingSpot1.getIndex() == 1) {
							nextParkingSpot = parkingSpotsHashMap.get("B[1]");
							int j = nState / 2;
							transition[i][j] = nextParkingSpot.getP();
							j += 2;
							transition[i][j] = Float.valueOf(decimalFormat
									.format(1 - nextParkingSpot.getP()));
						} else {
							nextParkingSpot = parkingSpotsHashMap.get("A["
									+ (parkingSpot1.getIndex() - 1) + "]");
							int j = ((i / 4) - 1) * 4;
							transition[i][j] = nextParkingSpot.getP();
							j += 2;
							transition[i][j] = Float.valueOf(decimalFormat
									.format(1 - nextParkingSpot.getP()));
						}
					} else {

						if (parkingSpot1.getIndex() == n / 2) {
							nextParkingSpot = parkingSpotsHashMap.get("A["
									+ (n / 2) + "]");
							int j = (nState / 8 - 1) * 4;
							transition[i][j] = nextParkingSpot.getP();
							j += 2;
							transition[i][j] = Float.valueOf(decimalFormat
									.format(1 - nextParkingSpot.getP()));

						} else {
							nextParkingSpot = parkingSpotsHashMap.get("B["
									+ (parkingSpot1.getIndex() + 1) + "]");
							int j = ((i / 4) + 1) * 4;
							transition[i][j] = nextParkingSpot.getP();
							j += 2;
							transition[i][j] = Float.valueOf(decimalFormat
									.format(1 - nextParkingSpot.getP()));
						}
					}
				}

			}
			Action action = new Action(1, "Drive", transition);
			mdp.actions.add(action);

			// /////////////////////////////////////////////////////////////////////////////////////////////////////
			// Defining the transition function for action park
			// ////////////////////////////////////////////////////////////////////////////////////////////////////
			transition = new float[mdp.states.size()][mdp.states.size()];
			for (int i = 0; i < mdp.states.size(); i++) {
				for (int j = 0; j < mdp.states.size(); j++) {
					transition[i][j] = 0;
					if (i == mdp.states.size() - 1
							&& j == mdp.states.size() - 1)
						transition[i][j] = 1;
				}
			}

			for (int i = 0; i < mdp.states.size(); i++) {
				State state1 = (State) mdp.states.get(i);
				if (state1.getId() != 4 * n && state1.parked == false) {
					if (state1.parked == false) {
						int j = i + 1;
						transition[i][j] = 1;
					}

				}

			}
			action = new Action(2, "Park", transition);
			mdp.actions.add(action);

			// /////////////////////////////////////////////////////////////////////////////////////////////////////
			// Defining the transition function for action exit
			// ////////////////////////////////////////////////////////////////////////////////////////////////////
			transition = new float[mdp.states.size()][mdp.states.size()];
			for (int i = 0; i < mdp.states.size(); i++) {
				for (int j = 0; j < mdp.states.size(); j++) {
					transition[i][j] = 0;
					if (i == mdp.states.size() - 1
							&& j == mdp.states.size() - 1)
						transition[i][j] = 1;
				}
			}

			for (int i = 0; i < mdp.states.size(); i++) {
				State state1 = (State) mdp.states.get(i);
				if (state1.parked == true) {
					transition[i][mdp.states.size() - 1] = 1;
				}
				if (i % 4 == 1 || i % 4 == 3) {
					transition[i][mdp.states.size() - 1] = 1;
				}
			}
			action = new Action(3, "Exit", transition);
			mdp.actions.add(action);
			if (debug == false)
				mdp.printMDP();
			return mdp;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

	public static void runParkingScenario(String[] args) {
		MDP mdp = generateParkingScenario(args[0]);
		StationaryPolicy policy = new StationaryPolicy();
		policy.setMDP(mdp);
		policy.valueIteration(0.000001f);
		for (State state : mdp.states) {
			System.out.println(state + "&" + policy.getAction(state) + "&"
					+ state.getValues().get(0) + "\\\\\\hline");
		}

	}

	public static void main(String[] args) {
		runParkingScenario(args);
	}
}
